#include "ftpclient.h"



CFtpClient :: CFtpClient()
{
   m_socketctrl = -1;
   m_sockdata = -1;

}



CFtpClient :: ~CFtpClient()
{
   close(m_socketctrl);
   close(m_sockdata);
}


//连接ftp服务器
int CFtpClient ::ftp_connect(const char* p , int port)
{

	struct addrinfo  hints;
    struct addrinfo *addrInfoList = NULL;
    struct addrinfo *cur          = NULL;
    int              fd           = 0;
    int              rc           = -1;
    char             service[6] = {0};
	int err = -1;

    memset(&hints, 0, sizeof(hints));

    hints.ai_family   = AF_INET; // only IPv4
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    snprintf(service, sizeof(service), "%u", port);
    MY_ERROR_LOG("connect ip:%s port:%d\r\n", p, port);
    if ((rc = getaddrinfo(p, service, &hints, &addrInfoList)) != 0) {    // 域名解析
        MY_ERROR_LOG("getaddrinfo error:%d\r\n", rc);
        return (int)-1;
    }
    for (cur = addrInfoList; cur != NULL; cur = cur->ai_next) {

        if (cur->ai_family != AF_INET) {
            MY_ERROR_LOG("socket type error\r\n");
            rc = -1;
            continue;
        }

        fd = socket(cur->ai_family, cur->ai_socktype, cur->ai_protocol);
        if (fd < 0) {
            MY_ERROR_LOG("create socket error\r\n");
            rc = -1;
            continue;
        }
        if (connect(fd, cur->ai_addr, cur->ai_addrlen) == 0) {
			
            rc = fd;
            break;
        }

        close(fd);
        MY_ERROR_LOG("connect error\r\n");
        rc = -1;
    }

    if (-1 == rc) {
        MY_ERROR_LOG("fail to establish tcp\r\n");
    } else {
        MY_ERROR_LOG("success to establish tcp, fd=%d\r\n", rc);
    }
    freeaddrinfo(addrInfoList);  
    m_socketctrl = rc;
    err = ftp_checkresp('2');
	if (err)
		return -1;
	return 0;
}

//登陆ftp服务器
int CFtpClient ::ftp_login(char* user, char* passwd)
{

	memset(m_cmd,0x00,sizeof(m_cmd));
	sprintf(m_cmd,"USER %s\r\n",user);
	int err = ftp_sendcmd();
	if (err)
		return -1;

	err = ftp_checkresp('3');
	if (err)
		return -1;

	//memset(m_cmd,0x00,sizeof(m_cmd));
	sprintf(m_cmd,"PASS %s\r\n",passwd);
	err = ftp_sendcmd();
	if (err)
		return -1;

	err = ftp_checkresp('2');
	if (err)
		return -1;

	return 0;

}

//显示当前目录
int CFtpClient ::ftp_pwd(char* buff)
{
   	memset(m_cmd, 0x00, sizeof(m_cmd));
	sprintf(m_cmd, "PWD\r\n");
	int err = ftp_sendcmd();
	if (err)
		return -1;

	err = ftp_checkresp('2');
	if (err)
		return -1;
	puts(m_resp);

	char *p = m_resp;

	
	while (*p)
	{
		if (*p++ == '"')
		{
			
			while (*p != '"')
				*buff++ = *p++;
			break;
		}
	}

	*buff = '\0';

	return 0;

}

//改变目录
int CFtpClient :: ftp_cd(char* dir)
{

	memset(m_cmd, 0x00, sizeof(m_cmd));
	sprintf(m_cmd,"CWD %s\r\n",dir);

	int err = ftp_sendcmd();
	if (err)
		return -1;

	err = ftp_checkresp('2');
	if (err)
		return -1;

	return 0;



}

//回到上一级目录
int CFtpClient :: ftp_cdup()
{

	memset(m_cmd, 0x00, sizeof(m_cmd));
	sprintf(m_cmd, "CDUP\r\n");

	int err = ftp_sendcmd();
	if (err)
		return -1;

	err = ftp_checkresp('2');
	if (err)
		return -1;

	return 0;


}

//创建目录
/*
 先去检测这个文件夹是否存在，如果存在则不创建，如果不存在则创建
 这样做的原因，可以通过结果来知道是否出现问题，否则如果要穿件的文件夹已存在，然后再去创建，就返回错误了。
*/
int CFtpClient :: ftp_mkdir(char * dir)
{
	vector<string> * mydir = new vector<string>;
	vector<string>::iterator iter;
	mydir = dirsplit(string(dir), string("/"));

	for (iter = mydir->begin(); iter != mydir->end(); ++iter)
	{
		int err = ftp_cd((char *)iter->c_str());
		if (err)
		{
			sprintf(m_cmd, "MKD %s\r\n", iter->c_str());
			int err = ftp_sendcmd();
			if (err)
				return -1;

			//如果文件夹已存在，则会创建失败
			err = ftp_checkresp('2');
			if (err)
				return -1;
		}
	}
		
	int err = ftp_cd("/");
	if (err)
		return -1;

	return 0;



}

//删除目录
int CFtpClient :: ftp_rmdir(char * dir)
{

	sprintf(m_cmd, "RMD %s\r\n", dir);
	int err = ftp_sendcmd();
	if (err)return -1;
	err = ftp_checkresp('2');
	if (err)return -1;
	return 0;


}

//数据传输模式
int CFtpClient :: ftp_setpasv()
{
	memset(m_cmd, 0x00, sizeof(m_cmd));
	sprintf(m_cmd,"PASV\r\n");
	int err = ftp_sendcmd();

	if (err)
		return -1;

	err = ftp_checkresp('2');
	if (err)
		return -1;

	
	m_sockdata = socket(AF_INET, SOCK_STREAM, 0);
	
	unsigned int v[6];

	
	struct sockaddr sa;
	//返回的例如是：121.111.22.154字符串转成整数(类似）
	sscanf(m_resp, "%*[^(](%u,%u,%u,%u,%u,%u", &v[2], &v[3], &v[4], &v[5], &v[0], &v[1]);
	
	sa.sa_family = AF_INET;
	
	for (int i = 0; i < 6; i++)
		sa.sa_data[i] = v[i];

	int on = 1;

	if (setsockopt(m_sockdata, SOL_SOCKET, SO_REUSEADDR,
		(const char*)&on, sizeof(on)) == -1)
	{
		MY_ERROR_LOG("error : %d \n", errno);
		close(m_sockdata);
		return -1;
	}
	struct linger lng = { 0, 0 };

	if (setsockopt(m_sockdata, SOL_SOCKET, SO_LINGER,
		(const char*)&lng, sizeof(lng)) == -1) //直接关闭，不经过四次挥手
	{
		MY_ERROR_LOG("error : %d \n", errno);;	
		close(m_sockdata);
		return -1;
	}

	err = connect(m_sockdata, (sockaddr*)&sa, sizeof(sa));
	if (err)
		return -1;
	return 0;


}

//下载文件
/*
	一条命令对应一个返回值，但是在下载的时候，
	发送RETR命令后：会收到两条
			：150 ok to send data （发送命令的结果）
			：226 Transfer complete（数据传输的结果）
*/

int CFtpClient :: ftp_download(char * remotefile, char* localfile)
{
	ftp_setpasv();
	memset(m_cmd, 0x00, sizeof(m_cmd));
	sprintf(m_cmd, "RETR %s\r\n", remotefile);
	int err = ftp_sendcmd();
	if (err)return -1;
	err = ftp_checkresp('1');
	if (err)
		return -1;
	FILE  * fp = fopen(localfile, "w");
	if (fp == NULL)
		return -1;

	char recbuf[256];
	int len = 0;
	while ((len = recv(m_sockdata, recbuf, 256, 0))>0)
	{
		err = fwrite(recbuf, len, 1, fp);
		if (len < 0)
			return -1;
	}

	close(m_sockdata);
	fclose(fp);

	err = ftp_checkresp('1'); //150 ok to send data
	if (err)
		return -1;


	err = ftp_checkresp('2'); //226 Transfer complete
	if (err)
		return -1;

	return 0;



}

/*
一条命令对应一个返回值，但是在下载的时候，
发送RETR命令后：会收到两条
：150 ok to send data （发送命令的结果）
：226 Transfer complete（数据传输的结果）
*/
int CFtpClient :: ftp_upload(char * localfile, char * remotepath, char * remotefilename)
{

    ftp_mkdir(remotepath);
	int err = ftp_cd(remotepath);
	if (err)
		return -1;

	ftp_setpasv();
	sprintf(m_cmd,"STOR %s\r\n",remotefilename);
	err = ftp_sendcmd();
		
	if (err)
		return -1;

	FILE *fp = fopen(localfile, "r");
	if (fp == NULL)
		return -1;

	char sendbuf[256];

	int len = 0;
	while ((len = fread(sendbuf, 1, 255, fp)) > 0)
	{
		err = send(m_sockdata, sendbuf, len, 0);
		if (err < 0)
			return -1;
	}

	close(m_sockdata);
	fclose(fp);

	err = ftp_checkresp('1');
	if (err)
		return -1;

	err = ftp_checkresp('2');
	if (err)
		return -1;
	return 0;

}

//退出登录
int CFtpClient :: ftp_quit()
{
	sprintf(m_cmd,"QUIT\r\n");
	int err = ftp_sendcmd();
	if (err)
		return -1;

	err = ftp_checkresp('2');
	if (err)
		return -1;
	return 0;


}

//列出目录的所有文件
int CFtpClient ::ftp_list(char* dir)
{

	ftp_setpasv();
	memset(m_cmd, 0x00, sizeof(m_cmd));
	sprintf(m_cmd, "NLST %s\r\n", dir);
	int err = ftp_sendcmd();
	if (err)return -1;
	err = ftp_checkresp('1');
	if (err)
		return -1;

	
	char recbuf[256];
	int len = 0;
	while ((len = recv(m_sockdata, recbuf, 256, 0))>0)
	{
		recbuf[len] = '\0';
		MY_ERROR_LOG("read len: %d %s\n",  len , recbuf);
	}

	vector<string> * list = new vector<string>;
	vector<string>::iterator iter;

	list = split(string(recbuf), "\r\n");
	
	for (iter = list->begin(); iter != list->end(); ++iter)
	{
		MY_ERROR_LOG("gain filename : %s\n", *iter);
	}
	
	close(m_sockdata);

	err = ftp_checkresp('2');
	if (err)
		return -1;
	return 0;


}

//接收返回状态，检测是否成功
int CFtpClient :: ftp_checkresp(char expresp) 
{

	memset(m_resp, 0x00, sizeof(m_resp));
	int len = recv(m_socketctrl, m_resp, 256, 0);
	if (len == -1) {
		cout << "check resp :" << len;
		return -1;
	}
	m_resp[len] = '\0';
	MY_ERROR_LOG("checkresp: %s\n", m_resp);

	if (m_resp[0] != expresp)
		return -1;
	return 0;


}

//发送指令 
int CFtpClient :: ftp_sendcmd()
{
	int ret = send(m_socketctrl,m_cmd,strlen(m_cmd),0);
	if (ret == -1){
		MY_ERROR_LOG("error: %d\n", errno);;
		return -1;
	}
	else
		return 0;


}

//splite功能的实现
vector<string>  * CFtpClient :: split(const string &str, const string &sep)
{

		vector<string> *svec = new vector<string>;
		string::size_type beg = 0, end = 0;
	
		beg = str.find_first_not_of(sep, end);
		while (beg != string::npos)
		{
			end = str.find_first_of(sep, beg);
			if (end == string::npos)
			{
				svec->push_back(string(str, beg));
				break;
			}
			else
			{
				svec->push_back(string(str, beg, end - beg));
				beg = str.find_first_not_of(sep, end);
			}
		}
	
		return svec;


}

//mkdir splite定制功能的实现
vector<string>	* CFtpClient :: dirsplit(const string &str, const string &sep)
{
	    vector<string> *svec = new vector<string>;
		string::size_type beg = 0, end = 0,start = 0;

		beg = str.find_first_not_of(sep, end);
		while (beg != string::npos)
		{
			end = str.find_first_of(sep, beg);
			if (end == string::npos)
			{
				svec->push_back(string(str, start));
				break;
			}
			else
			{
				svec->push_back(string(str, start, end));
				beg = str.find_first_not_of(sep, end);
			}
		}

		return svec;



}


